<?php
/**
 * Active record using Zend_Db_* classes.
 * 
 * @package Nuclide-Database
 * @author	Mats Lundberg
 * @version $Id$
 */ 

Zend_Loader::loadClass('Zend_Db_Table_Abstract');
Zend_Loader::loadClass('Zend_Db_Adapter_Abstract');
nuc_Loader::loadClass('database.nuc_Db_Table_Row_Abstract');

/**
 * Active record using Zend_Db_* classes.
 * 
 * Usage:
 * <code>
 * class User extends nuc_ActiveRecord 
 * {
 *	 protected $_name = 'users';
 *	 protected $_primary = 'user_id';
 * }
 * 
 * $user = new User();
 * $user->load($a_user_id);
 * $user->password = 'foo';
 * $user->save();
 * </code>
 * 
 * @package Nuclide-Database
 * @author	Mats Lundberg
 */
class nuc_ActiveRecord extends Zend_Db_Table_Abstract
{
	/**
	 * Record holder
	 *
	 * @var Zend_Db_Table_Row_Abstract
	 */
	protected $_record;
	/**
	 * Fetched from database
	 * 
	 * @var boolean
	 */
	protected $_stored = false;
	
	/**
	 * Constructor
	 * No type hinting because it generates exception
	 * 
	 * @param	Zend_Db_Adapter_Abstract	Database object to use for database access.
	 * @throws	nuc_ActiveRecordException
	 */
	public function __construct( Zend_Db_Adapter_Abstract $db ) {
		if( is_null( $db ) || !get_class( $db ) || !is_subclass_of( $db, 'Zend_Db_Adapter_Abstract' ) ) {
			throw new nuc_ActiveRecordException('Bad database adapter');
		}

		try {
			parent::__construct( $db );
		} catch( Exception $e ) {
			throw new nuc_ActiveRecordException('Could not construct the database table');
		}
	}
	
	/**
	 * Load record. Sends it arguments to Zend_Db_Table_Abstract->find()
	 * 
	 * @param	mixed	Multiple parameters allowed
	 * @return	boolean
	 */
	public function load() {
		try {
			$args = func_get_args();
			$rowset = call_user_func_array( array( $this, 'find' ), $args );
			
			if( !count( $rowset ) ) {
				return false;
			}
			
			$this->_record = new nuc_Db_Table_Row_Abstract( array(
				'table'		=> $rowset->current()->getTable(),
				'data'		=> $rowset->current()->toArray(),
				'stored'	=> true
			) );
			
			$this->_stored = true;
			
			return true;		
		} catch( Exception $e ) {
			throw $e;
		}
		
		return true;
	}
	
	/**
	 * Create new record. Done transparently when you try to access property.
	 */
	public function create() {
		$row = $this->createRow();

		$this->_record = new nuc_Db_Table_Row_Abstract( array(
			'table'	=> $row->getTable(),
			'data'	=> $row->toArray()
		) );
	}
	
	/**
	 * Save record
	 * 
	 * @return 	boolean
	 */
	public function save() {
		$ret = $this->_record->save();
		$this->_stored = true;
		
		return $ret;
	}
	
	/**
	 * Remove the active record
	 * 
	 * @throws	nuc_ActiveRecordException
	 */
	public function remove() {
		try {
			if( $this->_stored == false ) {
				throw new nuc_ActiveRecordException('Cannot remove a non-existing record');
			}		
		
			$numRows = $this->_record->delete();	
		} catch( Exception $e ) {
			throw new nuc_ActiveRecordException('Failed to delete record: '.$e->getMessage());
		}
	}
	
	/**
	 * Overloaded __set. Transparently creates record if it doesn't exist.
	 *
	 * @param	string	Column name
	 * @param	mixed	Value
	 */
	public function __set( $column, $value ) {
		if( is_null( $this->_record ) ) {
			$this->create();
		}
		
		$this->_record->$column = $value;
	}
	
	/**
	 * Get property
	 *
	 * @param	string	Column name
	 * @return	mixed
	 */
	public function __get( $column ) {
		if( is_null( $this->_record ) ) {
			$this->create();
		}
		
		return $this->_record->$column;
	}
	
	/**
	 * Overloaded __isset.
	 * 
	 * @param	string	Column name
	 * @return	boolean
	 */
	public function __isset( $column ) {
		return isset( $this->_record->$column ) && !is_null( $this->_record->$column );
	}
	
	/**
	 * Clears _cleanData array so that records can be inserted multiple times instead
	 * of updating.
	 */
	public function clearCleanData() {
		if( !is_null( $this->_record ) ) {
			$this->_record->clearCleanData();
		}
	}
	
	/**
	 * Get columns
	 *
	 * @return	array
	 */
	public function getColumns() {
		return $this->_getCols();
	}
	
	/**
	 * Return record as array
	 *
	 * @return array
	 */
	public function toArray() {
		return $this->_record->toArray();
	}	
	
	/**
	 * magic clone function. Not tested very much! Currently disabled.
	 */
	public function __clone() {
		$this->clearCleanData();
		$this->_stored = false;
		
		$primary = $this->_getPrimary();

		// Copy all necessary meta data
		$objectVars = array_keys( get_object_vars( $this ) );
		
		foreach( $objectVars as $key ) {
			$this->$key = is_object( $this->$key ) ? clone $this->$key : $this->$key;
		}
		
		// Copy data columns
		foreach( $this->getColumns() as $key ) {
			if( $key != $primary ) {
				$this->$key = is_object( $this->$key ) ? clone $this->$key : $this->$key;
			} else {
				$this->$key = null;
			}
		}
	}
	
	/**
	 * Do not serialize database objects (enable pdo_* support)
	 *
	 * @return array of serializable variables
	 */
	public function __sleep() {
		$ret = array();
		
		foreach( get_object_vars( $this ) as $name => $var ) {
			if( !is_object( $var ) || !is_subclass_of( $var, 'Zend_Db_Adapter_Abstract' ) ) {
				$ret[] = $name;
			}
		}
		
		return $ret;
	}
}

/**
 * Exception for nuc_ActiveRecord
 * 
 * @package		Nuclide-Database
 * @subpackage	Exceptions
 */ 
class nuc_ActiveRecordException extends Exception {}
?>
